package com.wuzao.proxy;

/**
 * <p>
 *
 * </p>
 *
 * @author wuzuhao
 * @since 2023-08-03
 */

import com.wuzao.service.Calculator;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 这个类就是帮Calculator生成代理对象的类
 * static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
 * 返回指定接口的代理类的实例，该接口将方法调用分派给指定的调用处理程序。
 */
public class CalculatorProxyImprove {
    /**
     * 为传入的参数创建一个代理对象
     * 传入的Calculator calculator：被代理对象
     * 返回：代理对象:return proxy;
     */
// 这里getproxy里面传入的参数要加final，是因为下面的InvocationHandler里面要用到calculator这个参数，具体下面有解释
    public static Calculator getProxy(final Calculator calculator) {

        // proxy为目标对象创建代理对象
        /**
         * newProxyInstance有三个参数
         * InvocationHandler :方法执行器，帮我们的目标对象执行目标方法
         * Class<?>[] interfaces：目标对象实现了的接口
         * ClassLoader loader：目标对象的类加载器
         */

        Object proxy = Proxy.newProxyInstance(calculator.getClass().getClassLoader(), calculator.getClass().getInterfaces(), new InvocationHandler() {

            /**
             * new一个InvocaotionHandler，这是一个匿名内部类，有三个参数
             * Object proxy：代理对象，给jdk使用的，任何时候我们都不要动这个东西
             * Method method：当前将要执行的目标对象的方法
             *  Object[] args：这个方法调用时，外界传入的参数
             */

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {


                // 利用反射使用 method.invoke ，执行目标方法
                // menthod.invoke 里面有两个参数：
                // object：这里是calculator，就是执行哪个对象，为哪个对象创建代理对象，就填哪个对象，但是有一个规定：如果要在
                // 内部类里面要用一个声明的参数，要加final，所以要在getProxy方法里面的calculator参数前面加final
                // args：目标方法里面的参数
                // 返回一个result：这个result就是目标方法执行完后的返回值，也就是加减乘除执行完后的返回值
                Object result = null;
                try {
                    // 加入日志信息(方法开始前)，Arrays.aslist是将args参数显示出来，如果不用array.aslist，控制台会显示其地址值
                    System.out.println("["+method.getName()+"]开始执行.....它的参数是："+ Arrays.asList(args)+"");
                    result = method.invoke(calculator, args);
                    // 加入日志信息(方法开始后)
                    System.out.println("["+method.getName()+"]正常执行完毕.....，结果是："+result+"");

                }
                catch(Exception e){
                    System.out.println("["+method.getName()+"]出现异常："+e.toString()+"");
                }
                finally {
                    System.out.println("方法最终执行完毕");
                }

                // 这个返回值必须返回出去，外界才能拿到真正执行后的返回值
                return result;
            }
        });
        return (Calculator) proxy;

    }
}
